package com.typingtutor.ui.tabs;

import com.typingtutor.dto.*;
import com.typingtutor.model.*;
import com.typingtutor.ui.KanaCanvas;
import com.typingtutor.ui.TypingTutor;
import com.typingtutor.util.ReflectionExample;
import lombok.Getter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.tree.DefaultMutableTreeNode;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;

public class TypingPracticeTab {
    @Getter
    private JPanel panel;
    private KanaCanvas kanaCanvas;
    private JTextField inputField;
    private JLabel feedbackLabel;
    private JLabel resultLabel;
    private TypingTutor mainFrame;
    private DefaultMutableTreeNode selectedFolderNode;
    private FieldMapping currentFieldMapping;
    @Getter
    private static StatisticsChartTab statisticsChartTab;

    // 内存缓存记住的单词ID
    private final Set<String> memorizedWords = new HashSet<>();
    private JButton memorizeButton;
    private JButton clearMemorizedButton;

    public TypingPracticeTab(TypingTutor mainFrame) {
        this.mainFrame = mainFrame;
        initComponents();
    }

    public TypingPracticeTab(TypingTutor mainFrame, DefaultMutableTreeNode selectedFolderNode) {
        this(mainFrame);
        this.selectedFolderNode = selectedFolderNode;
        loadData();
    }

    private void initComponents() {
        panel = new JPanel(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.insets = new Insets(10, 10, 10, 10);
        gbc.weightx = 1.0; // 水平扩展

        Font defaultFont = new Font("微软雅黑", Font.PLAIN, 16);
        Font largeFont = defaultFont.deriveFont(Font.PLAIN, 24);

        //  kanaCanvas 区域
        kanaCanvas = new KanaCanvas(largeFont);
        addComponentToPanel(kanaCanvas, gbc, 0, 0);

        // 输入框区域
        addInputField(gbc, defaultFont);

        // 反馈标签区域
        addFeedbackLabel(gbc, defaultFont);

        // 结果标签区域
        addResultLabel(gbc, defaultFont);

        // 按钮面板（包含所有按钮）
        addButtonPanel(gbc);
    }

    private void addComponentToPanel(Component component, GridBagConstraints gbc, int gridy, int insetsTop) {
        gbc.gridy = gridy;
        gbc.insets = new Insets(insetsTop, 10, 10, 10);
        panel.add(component, gbc);
    }

    private void addInputField(GridBagConstraints gbc, Font font) {
        gbc.gridy++;
        gbc.insets = new Insets(0, 10, 0, 10);
        inputField = new JTextField(30);
        inputField.addActionListener(new TypingListener());
        inputField.setFont(font);
        JPanel inputPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
        inputPanel.add(inputField);
        panel.add(inputPanel, gbc);
    }

    private void addFeedbackLabel(GridBagConstraints gbc, Font font) {
        gbc.gridy++;
        gbc.insets = new Insets(20, 10, 0, 10);
        feedbackLabel = new JLabel();
        feedbackLabel.setHorizontalAlignment(JLabel.CENTER);
        feedbackLabel.setFont(font);
        panel.add(feedbackLabel, gbc);
    }

    private void addResultLabel(GridBagConstraints gbc, Font font) {
        gbc.gridy++;
        gbc.insets = new Insets(10, 10, 10, 10);
        resultLabel = new JLabel("请开始打字练习...");
        resultLabel.setHorizontalAlignment(JLabel.CENTER);
        resultLabel.setFont(font);
        panel.add(resultLabel, gbc);
    }

    private void addButtonPanel(GridBagConstraints gbc) {
        gbc.gridy++;
        gbc.insets = new Insets(10, 10, 10, 10);

        JPanel buttonPanel = new JPanel(new GridLayout(0, 2, 15, 10)); // 2列，水平/垂直间距15/10
        buttonPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); // 移除边框

        // 统计图表按钮
        addButtonToPanel(buttonPanel, "统计图表", e ->
                TypingTutor.addTab("统计图表", statisticsChartTab == null ?
                        (statisticsChartTab = new StatisticsChartTab(mainFrame)).getPanel() :
                        statisticsChartTab.getPanel()
                ));

        // 错误记录按钮
        addButtonToPanel(buttonPanel, "查看错误记录", this::showErrorRecordsInTable);

        // 记住单词按钮
        memorizeButton = addButtonToPanel(buttonPanel, "记住该单词", this::handleMemorizeAction);
        memorizeButton.setEnabled(false); // 初始禁用

        // 清除已记住按钮
        addButtonToPanel(buttonPanel, "重置已记住单词", this::clearMemorizedWords);

        panel.add(buttonPanel, gbc);
    }

    private JButton addButtonToPanel(JPanel panel, String text, ActionListener listener) {
        JButton button = new JButton(text);
        button.setFont(new Font("微软雅黑", Font.PLAIN, 14));
        button.addActionListener(listener);
        panel.add(button);
        return button;
    }

    public void loadData() {
        showCurrentCard();
        resetInputFields();
    }

    private void showCurrentCard() {
        if (isValidSelectedFolderNode()) {
            Folder folder = getSelectedFolder();
            List<RandomRule> rules = getEnabledRandomRules(folder);

            if (CollectionUtils.isNotEmpty(rules)) {
                rules.forEach(this::processRandomRule);
            }
        }
    }

    private boolean isValidSelectedFolderNode() {
        return selectedFolderNode != null && selectedFolderNode instanceof Folder;
    }

    private Folder getSelectedFolder() {
        return (Folder) selectedFolderNode;
    }

    private List<RandomRule> getEnabledRandomRules(Folder folder) {
        return RandomRuleDAO.getInstance().findEnabledRandomByFolderId(folder.getId());
    }

    private void addErrorStatsButton(GridBagConstraints gbc) {
        // 该方法不再使用，按钮统一由 addButtonPanel 管理
    }

    /**
     * 显示错误记录表格对话框
     */
    private void showErrorRecordsInTable(ActionEvent e) {
        JDialog dialog = createErrorRecordsDialog();
        loadErrorRecordsIntoModel(dialog);
        dialog.setVisible(true);
    }

    /**
     * 创建错误记录对话框
     */
    private JDialog createErrorRecordsDialog() {
        JDialog dialog = new JDialog(mainFrame, "错误记录统计", true);
        dialog.setSize(800, 600);
        dialog.setLocationRelativeTo(mainFrame);
        dialog.setLayout(new BorderLayout());
        return dialog;
    }

    /**
     * 创建表格模型
     */
    private DefaultTableModel createTableModel() {
        return new DefaultTableModel(
                new String[]{"内容", "错误次数", "日期"}, 0
        );
    }

    /**
     * 获取错误记录
     */
    private List<TypingData> fetchErrorRecords() {
        return TypingDataDAO.getInstance().getErrorRecords();
    }

    /**
     * 将错误记录转换为表格行数据
     */
    private Object[] convertRecordToRowData(TypingData record) {
        String errorCountStr = record.getInputContent().replace("错误次数: ", "");
        int errorCount = Integer.parseInt(errorCountStr);
        return new Object[]{
                record.getCorrectAnswer(),
                errorCount,
                record.getRecordTime()
        };
    }

    /**
     * 创建并配置表格
     */
    private JTable createConfiguredTable(DefaultTableModel model) {
        JTable table = new JTable(model);
        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        table.setAutoCreateRowSorter(true);
        return table;
    }

    /**
     * 创建关闭按钮面板
     */
    private JPanel createCloseButtonPanel(JDialog dialog) {
        JPanel panel = new JPanel();
        JButton closeButton = new JButton("关闭");
        closeButton.addActionListener(e -> dialog.dispose());
        panel.add(closeButton);
        return panel;
    }

    private void loadErrorRecordsIntoModel(JDialog dialog) {
        DefaultTableModel model = createTableModel();
        List<TypingData> errorRecords = fetchErrorRecords();

        errorRecords.forEach(record -> model.addRow(convertRecordToRowData(record)));

        JTable table = createConfiguredTable(model);
        dialog.add(new JScrollPane(table), BorderLayout.CENTER);
        dialog.add(createCloseButtonPanel(dialog), BorderLayout.SOUTH);
    }

    private void processRandomRule(RandomRule rule) {
        List<Folder> subFolders = getSubFolders(rule);
        List<String> folderIds = extractFolderIds(subFolders);
        List<FieldMapping> fields = getFieldMappings(folderIds);

        // 过滤已记住的单词
        fields = fields.stream()
                .filter(f -> !memorizedWords.contains(f.getDocumentId()))
                .collect(Collectors.toList());

        List<FieldKeyInfo> infos = getFieldKeyInfos(rule.getId());
        String filterIds = buildFilterIdString(infos);
        List<FieldMapping> filteredFields = filterFields(fields, filterIds);

        currentFieldMapping = selectRandomFieldMapping(filteredFields);

        // 更新按钮状态
        memorizeButton.setEnabled(currentFieldMapping != null);
        updateKanaCanvas(filteredFields);
    }

    private void handleMemorizeAction(ActionEvent e) {
        if (currentFieldMapping != null) {
            memorizedWords.add(currentFieldMapping.getDocumentId());
            JOptionPane.showMessageDialog(panel,
                    "该单词已加入记住列表，不再随机出现",
                    "操作成功",
                    JOptionPane.INFORMATION_MESSAGE);
            loadData(); // 重新加载以排除记住的单词
        }
    }

    private void clearMemorizedWords(ActionEvent e) {
        memorizedWords.clear();
        JOptionPane.showMessageDialog(panel, "已清除所有记住的单词", "操作成功", JOptionPane.INFORMATION_MESSAGE);
        loadData();
    }

    private List<Folder> getSubFolders(RandomRule rule) {
        return FolderDAO.getInstance().findAllSubFolderByIdAndFolderName(
                rule.getFolderId(),
                rule.getRandomRange()
        );
    }

    private List<String> extractFolderIds(List<Folder> subFolders) {
        return subFolders.stream().map(Folder::getId).collect(Collectors.toList());
    }

    private List<FieldMapping> getFieldMappings(List<String> folderIds) {
        return DocumentDAO.getInstance().findFieldMappingsByFolderId(folderIds);
    }

    private List<FieldKeyInfo> getFieldKeyInfos(String ruleId) {
        return FieldKeyInfoDAO.getInstance().findFieldKeyInfoByFolderRandomRulesId(ruleId);
    }

    private String buildFilterIdString(List<FieldKeyInfo> infos) {
        StringJoiner joiner = new StringJoiner(",");
        infos.forEach(info -> joiner.add(getFieldKeyInfoIds(info.getFieldKey())));
        return joiner.toString();
    }

    private String getFieldKeyInfoIds(String fieldKey) {
        return FieldKeyInfoDAO.getInstance().findAllFieldKeyByFolderId(
                        getSelectedFolder().getId()
                ).stream()
                .filter(it -> it.getFieldKey().equals(fieldKey))
                .map(FieldKeyInfo::getId)
                .collect(Collectors.joining());
    }

    private List<FieldMapping> filterFields(List<FieldMapping> fields, String filterIds) {
        List<String> displayIds = FieldMappingDAO.getInstance().findDisplayFieldMappingId(filterIds);
        return fields.stream()
                .filter(it -> displayIds.contains(it.getId()))
                .collect(Collectors.toList());
    }

    private FieldMapping selectRandomFieldMapping(List<FieldMapping> fields) {
        return fields.isEmpty() ? null : fields.get(new Random().nextInt(fields.size()));
    }

    private void updateKanaCanvas(List<FieldMapping> fields) {
        List<FieldMapping> showMappings = filterByDocumentId(fields);
        List<String> values = extractFieldValues(showMappings);
        kanaCanvas.setTextList(values);
        kanaCanvas.repaint();
    }

    private List<FieldMapping> filterByDocumentId(List<FieldMapping> fields) {
        return fields.stream()
                .filter(it -> it.getDocumentId().equals(currentFieldMapping.getDocumentId()))
                .collect(Collectors.toList());
    }

    private List<String> extractFieldValues(List<FieldMapping> mappings) {
        return ReflectionExample.getNonNullFieldValuesFromList(
                        mappings,
                        Collections.singletonList("fieldValue")
                ).stream()
                .map(Object::toString)
                .collect(Collectors.toList());
    }

    public static <T> T nextRandomElement(List<T> list) {
        if (list == null || list.isEmpty()) return null;
        return list.get(new Random().nextInt(list.size()));
    }

    private class TypingListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            String input = inputField.getText().trim();
            if (isInputEmpty(input)) {
                showInputEmptyFeedback();
                return;
            }

            if (currentFieldMapping != null && isValidSelectedFolderNode()) {
                Folder folder = getSelectedFolder();
                List<RandomRule> rules = getEnabledRandomRules(folder);
                rules.forEach(rule -> validateInput(rule, input));
            }
            updateResultLabel();
        }

        private boolean isInputEmpty(String input) {
            return StringUtils.isBlank(input);
        }

        private void showInputEmptyFeedback() {
            feedbackLabel.setForeground(Color.RED);
            feedbackLabel.setText("请输入内容！");
            inputField.setText("");
        }

        private void validateInput(RandomRule rule, String input) {
            List<FieldKeyInfo> keyInfos = getFieldKeyInfosByRule(rule.getId());
            keyInfos.stream()
                    .filter(info -> info.getFieldKey().equals(rule.getMathField()))
                    .forEach(info -> processWordInput(info, rule, input));
        }

        private List<FieldKeyInfo> getFieldKeyInfosByRule(String ruleId) {
            return FieldKeyInfoDAO.getInstance().findFieldKeyInfoByRandomRuleId(ruleId);
        }

        private void processWordInput(FieldKeyInfo info, RandomRule rule, String input) {
            Word word = getWordByInfoAndMapping(info);
            if (word == null) return;

            if (word.getFieldValue().contains("/")) {
                handleSlashInput(word, input, rule);
            } else if (input.equals(word.getFieldValue())) {
                inputCorrect(word, rule);
            } else {
                inputError(word, rule, input);
            }
        }

        private Word getWordByInfoAndMapping(FieldKeyInfo info) {
            return FieldMappingDAO.getInstance().findFieldValueByFieldKeyIdAndFieldMappingId(
                    info.getId(),
                    currentFieldMapping.getId()
            );
        }

        private void handleSlashInput(Word word, String input, RandomRule rule) {
            String[] options = word.getFieldValue().split("/");
            if (input.equals(options[0]) || input.equals(options[1])) {
                inputCorrect(word, rule);
            } else {
                inputError(word, rule, input);
            }
        }
    }

    public void resetInput() {
        inputField.setText("");
        resultLabel.setText("请开始打字练习...");
    }

    public void showErrorMessage(String message) {
        resultLabel.setText(message);
    }

    public void inputCorrect(Word word, RandomRule item) {
        feedbackLabel.setForeground(Color.GREEN);
        feedbackLabel.setText("输入正确！");
        recordData(word, item, null, "correct");
        showCurrentCard();
        inputField.setText("");
    }

    public void inputError(Word word, RandomRule item, String userInput) {
        if (word != null) {
            feedbackLabel.setForeground(Color.RED);
            feedbackLabel.setText("输入错误！正确答案：" + getCorrectAnswer(word, item));
            recordData(word, item, userInput, "error");
        } else {
            feedbackLabel.setForeground(Color.RED);
            feedbackLabel.setText("输入错误！未找到正确答案");
        }
        inputField.setText("");
    }

    private void recordData(Word word, RandomRule item, String type) {
        String correctAnswer = getCorrectAnswer(word, item);
        TypingDataDAO.getInstance().recordData(
                type.equals("correct") ? null : inputField.getText().trim(),
                correctAnswer,
                type
        );
    }

    private void recordData(Word word, RandomRule item, String userInput, String type) {
        String correctAnswer = getCorrectAnswer(word, item);
        TypingDataDAO.getInstance().recordData(userInput, correctAnswer, type);
    }

    private String getCorrectAnswer(Word word, RandomRule item) {
        FieldMapping mapping = getChineseFieldMapping(item);
        return mapping != null
                ? word.getFieldValue() + "-" + mapping.getFieldValue()
                : word.getFieldValue();
    }

    private FieldMapping getChineseFieldMapping(RandomRule item) {
        return FieldMappingDAO.getInstance().findByRandomIdAndFieldKeyIsChineseAndDocumentId(
                item.getId(),
                currentFieldMapping.getDocumentId()
        );
    }

    private void resetInputFields() {
        inputField.setText("");
        resultLabel.setText("请开始打字练习...");
    }

    private void updateResultLabel() {
        SwingUtilities.invokeLater(() -> {
            TypingDataDAO dao = TypingDataDAO.getInstance();
            int correctCount = dao.getCorrectRecords().size();
            int errorCount = dao.getErrorRecords().size();
            int total = correctCount + errorCount;

            double accuracy = total > 0 ? (double) correctCount / total * 100 : 0;
            resultLabel.setText(String.format("正确率: %.2f%% (%d/%d)", accuracy, correctCount, total));
        });
    }

    private void showErrorRecords() {
        List<TypingData> errors = TypingDataDAO.getInstance().getErrorRecords();

        if (errors.isEmpty()) {
            JOptionPane.showMessageDialog(panel, "暂无错误记录", "错误统计", JOptionPane.INFORMATION_MESSAGE);
            return;
        }

        StringBuilder message = new StringBuilder("错误记录:\n\n");
        for (TypingData error : errors) {
            message.append("输入: ").append(error.getInputContent()).append("\n");
            message.append("正确答案: ").append(error.getCorrectAnswer()).append("\n");
            message.append("时间: ").append(error.getRecordTime()).append("\n\n");
        }

        JTextArea textArea = new JTextArea(message.toString());
        textArea.setEditable(false);
        JScrollPane scrollPane = new JScrollPane(textArea);
        scrollPane.setPreferredSize(new Dimension(400, 300));

        JOptionPane.showMessageDialog(
                panel,
                scrollPane,
                "错误记录详情",
                JOptionPane.INFORMATION_MESSAGE
        );
    }
}